﻿/*********************************************************************************
**                                                                              **
**  Copyright (C) 2024-2025 LiLong                                              **
**  This file is part of OpenVisaApplication.                                   **
**                                                                              **
**  OpenVisaApplication is free software: you can redistribute it and/or modify **
**  it under the terms of the GNU General Public License as published by        **
**  the Free Software Foundation, either version 3 of the License, or           **
**  (at your option) any later version.                                         **
**                                                                              **
**  OpenVisaApplication is distributed in the hope that it will be useful,      **
**  but WITHOUT ANY WARRANTY; without even the implied warranty of              **
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               **
**  GNU General Public License for more details.                                **
**                                                                              **
**  You should have received a copy of the GNU General Public License           **
**  along with OpenVisaApplication. If not, see <https://www.gnu.org/licenses/>.**
**********************************************************************************/
#include "TestModel.h"

#include <QApplication>
#include <QClipboard>

#include <ranges>

struct TestModel::Impl
{
    int rowCount = MaxRows;
    std::map<unsigned int, std::tuple<QString, QString, QString>> datas;
    QStringList headers { u8"指令", u8"返回值", u8"注释" };
};

TestModel::TestModel(QObject* parent) : QAbstractTableModel(parent), m_impl(std::make_unique<Impl>()) {}

TestModel::~TestModel() {}

int TestModel::rowCount(const QModelIndex& parent /* = QModelIndex() */) const { return m_impl->rowCount; }

int TestModel::columnCount(const QModelIndex& parent /* = QModelIndex() */) const { return m_impl->headers.size(); }

bool TestModel::setData(const QModelIndex& index, const QVariant& value, int role /* = Qt::EditRole */)
{
    if (role == Qt::EditRole)
    {
        switch (static_cast<Columns>(index.column()))
        {
        case Columns::Command:
            std::get<0>(m_impl->datas[index.row()]) = value.toString().trimmed();
            break;
        case Columns::Result:
            std::get<1>(m_impl->datas[index.row()]) = value.toString();
            break;
        case Columns::Comment:
            std::get<2>(m_impl->datas[index.row()]) = value.toString();
            break;
        }
        emit dataChanged(index, index, { role });
        return true;
    }
    return false;
}

QVariant TestModel::data(const QModelIndex& index, int role /* = Qt::DisplayRole */) const
{
    if (role == Qt::DisplayRole || role == Qt::EditRole)
    {
        switch (static_cast<Columns>(index.column()))
        {
        case Columns::Command:
            return std::get<0>(m_impl->datas[index.row()]);
        case Columns::Result:
            return std::get<1>(m_impl->datas[index.row()]);
        case Columns::Comment:
            return std::get<2>(m_impl->datas[index.row()]);
        }
    }
    return {};
}

QVariant TestModel::headerData(int section, Qt::Orientation orientation, int role /* = Qt::DisplayRole */) const
{
    if (role == Qt::DisplayRole)
    {
        return orientation == Qt::Horizontal ? QVariant(m_impl->headers[section]) : section + 1;
    }
    return {};
}

void TestModel::setDatas(const std::map<unsigned int, std::tuple<QString, QString, QString>>& datas, int rowCount)
{
    beginResetModel();
    m_impl->rowCount = rowCount;
    m_impl->datas    = datas;
    endResetModel();
}

Qt::ItemFlags TestModel::flags(const QModelIndex& index) const { return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; }

void TestModel::clearResults()
{
    beginResetModel();
    for (auto& [_, result, __] : m_impl->datas | std::views::values)
    {
        result.clear();
    }
    endResetModel();
}

void TestModel::paste(int row, const QString& text)
{
    if (text.isEmpty())
        return;
    auto texts = text.split('\n', Qt::SkipEmptyParts);
    for (auto i = row; const auto& txt : texts)
    {
        auto& d        = m_impl->datas[i++];
        std::get<0>(d) = txt;
    }
    emit dataChanged(index(row, 0), index(row + texts.size(), 0));
}

void TestModel::copy(const QModelIndexList& indexes)
{
    QStringList copiedData;
    for (int row = -1; const auto& index : indexes)
    {
        if (auto it = m_impl->datas.find(index.row()); it != m_impl->datas.end())
        {
            const auto& t = it->second;
            QString txt;
            switch (static_cast<Columns>(index.column()))
            {
            case Columns::Command:
                txt = std::get<static_cast<int>(Columns::Command)>(t).trimmed();
                break;
            case Columns::Result:
                txt = std::get<static_cast<int>(Columns::Result)>(t).trimmed();
                break;
            case Columns::Comment:
                txt = std::get<static_cast<int>(Columns::Comment)>(t).trimmed();
                break;
            }
            if (txt.isEmpty())
                continue;
            if (row != index.row())
            {
                copiedData.append(txt);
                row = index.row();
            }
            else
            {
                copiedData.back().append('\t');
                copiedData.back().append(txt);
            }
        }
    }
    qApp->clipboard()->setText(copiedData.join('\n'));
}

void TestModel::remove(const QModelIndexList& indexes)
{
    if (indexes.isEmpty())
        return;
    for (int row = -1; const auto& index : indexes)
    {
        if (auto it = m_impl->datas.find(index.row()); it != m_impl->datas.end())
        {
            auto& t = it->second;
            switch (static_cast<Columns>(index.column()))
            {
            case Columns::Command:
                std::get<static_cast<int>(Columns::Command)>(t).clear();
                break;
            case Columns::Result:
                std::get<static_cast<int>(Columns::Result)>(t).clear();
                break;
            case Columns::Comment:
                std::get<static_cast<int>(Columns::Comment)>(t).clear();
                break;
            }
        }
    }
    emit dataChanged(indexes.front(), indexes.back());
}
